package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author atguigu-mqx
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper,OrderInfo> implements OrderService {

    /*
    ware:
        url: http://localhost:9001
    */
    @Value("${ware.url}")
    private String wareUrl;


    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrderInfo(OrderInfo orderInfo) {
        //  orderInfo orderDetail
        //  total_amount order_status user_id out_trade_no trade_body operate_time expire_time process_status
        orderInfo.sumTotalAmount();
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //  这个第三方交易编号不能重复！
        String outTradeNo = "ATGUIGU" + System.currentTimeMillis() + "" + new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        //  把订单明细中的各个商品名称拼接起来。
        orderInfo.setTradeBody("购买小分队.....");
        orderInfo.setOperateTime(new Date());
        //  设置过期时间：
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE,1);
        orderInfo.setExpireTime(calendar.getTime());
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        orderInfoMapper.insert(orderInfo);

        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        //  返回订单Id
        Long orderId = orderInfo.getId();
        //  插入订单明细！
        if (!CollectionUtils.isEmpty(orderDetailList)){
            for (OrderDetail orderDetail : orderDetailList) {
                orderDetail.setOrderId(orderId);
                orderDetailMapper.insert(orderDetail);
            }
        }
        //  发送一个延迟消息： 3秒钟不支付就取消！
        this.rabbitService.sendDelayMsg(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,MqConst.ROUTING_ORDER_CANCEL,orderId,MqConst.DELAY_TIME);
        return orderId;
    }

    @Override
    public String getTradeNo(String userId) {
        //  生成流水号
        String tradeNo = UUID.randomUUID().toString();
        //  放入缓存！
        //  set list String
        String tradeNoKey = "tradeNo:"+userId;
        this.redisTemplate.opsForValue().set(tradeNoKey,tradeNo);
        return tradeNo;
    }

    @Override
    public Boolean checkTradeNo(String tradeNo, String userId) {
        //  前端传递的tradeNo
        String tradeNoKey = "tradeNo:"+userId;
        String redisTradeNo = (String) this.redisTemplate.opsForValue().get(tradeNoKey);
        //  返回比较结果！
        return tradeNo.equals(redisTradeNo);
    }

    @Override
    public void delTradeNo(String userId) {
        //  删除key
        this.redisTemplate.delete("tradeNo:"+userId);
    }

    @Override
    public Boolean checkStock(Long skuId, Integer skuNum) {
        //  远程调用  wareUrl = http://localhost:9001/hasStock?skuId=10221&num=2
        String result = HttpClientUtil.doGet(wareUrl + "/hasStock?skuId=" + skuId + "&num=" + skuNum);
        //  判断返回
        return "1".equals(result);
    }

    @Override
    public IPage getOrderList(Page<OrderInfo> orderInfoPage, String userId) {
        //  查询orderInfo，orderDetail 因此可能需要设计到多表关联查询！ orderInfo ： orderDetail  1对多
        //  方法一：使用mapper 查询
        //        QueryWrapper<OrderInfo> orderInfoQueryWrapper = new QueryWrapper<>();
        //        orderInfoQueryWrapper.eq("user_id",userId);
        //        Page<OrderInfo> infoPage = orderInfoMapper.selectPage(orderInfoPage, orderInfoQueryWrapper);
        //  需要再次查询detail!

        //  方法二：使用sql 语句！
        IPage<OrderInfo> iPage = orderInfoMapper.selectOrderList(orderInfoPage,userId);
        //  根据订单状态获取到对应的状态中文名
        iPage.getRecords().forEach((item)->{
            item.setOrderStatusName(OrderStatus.getStatusNameByStatus(item.getOrderStatus()));
        });
        return iPage;
    }

    @Override
    public void execExpiredOrder(Long orderId) {
        //  修改状态！
        //        OrderInfo orderInfo = new OrderInfo();
        //        orderInfo.setId(orderId);
        //        //  更新订单状态.
        //        orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
        //        //  更新订单的进度状态.
        //        orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());
        //        //  执行sql 语句.
        //        orderInfoMapper.updateById(orderInfo);

        //  进行优化：
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);

        //  应该判断：是否有paymentInfo .

        //  发送消息关闭paymentInfo
        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);


    }

    /**
     * 更新订单状态.
     * @param orderId
     * @param processStatus
     */
    @Override
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        //  更新订单状态.
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        //  更新订单的进度状态.
        orderInfo.setProcessStatus(processStatus.name());
        //  执行sql 语句.
        orderInfoMapper.updateById(orderInfo);
    }

    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        //  调用mapper 层.
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);

        if (orderInfo!=null){
            QueryWrapper<OrderDetail> orderDetailQueryWrapper = new QueryWrapper<>();
            orderDetailQueryWrapper.eq("order_id",orderId);
            orderInfo.setOrderDetailList(orderDetailMapper.selectList(orderDetailQueryWrapper));
        }
        //  返回数据。
        return orderInfo;
    }

    @Override
    public void sendOrderStatus(Long orderId) {
        //  必须更新订单状态.
        this.updateOrderStatus(orderId,ProcessStatus.NOTIFIED_WARE);

        //  组装发送消息的内容.
        OrderInfo orderInfo = this.getOrderInfo(orderId);

        //  将orderInfo 中的部分数据转换为map,再转换为Json 数据发送出去.
        Map map = this.initWare(orderInfo);

        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_WARE_STOCK,MqConst.ROUTING_WARE_STOCK, JSON.toJSONString(map));

    }

    /**
     * 将orderInfo 转换map ，后续会使用这个方法.
     * @param orderInfo
     * @return
     */
    public Map initWare(OrderInfo orderInfo) {
        /*
        1.  声明一个map 集合.
        2.  给map 集合设置key 对应的value.
        3.  返回map.
         */
        HashMap<String, Object> map = new HashMap<>();
        map.put("orderId",orderInfo.getId());
        map.put("consignee",orderInfo.getConsignee());
        map.put("consigneeTel",orderInfo.getConsigneeTel());
        map.put("orderComment",orderInfo.getOrderComment());
        map.put("orderBody",orderInfo.getTradeBody());
        map.put("deliveryAddress",orderInfo.getDeliveryAddress());
        map.put("paymentWay","2");
        map.put("wareId",orderInfo.getWareId());


        List<HashMap<String, Object>> maps = orderInfo.getOrderDetailList().stream().map(orderDetail -> {
            HashMap<String, Object> detailMap = new HashMap<>();
            detailMap.put("skuId", orderDetail.getSkuId());
            detailMap.put("skuName", orderDetail.getSkuName());
            detailMap.put("skuNum", orderDetail.getSkuNum());
            //  返回map
            return detailMap;
        }).collect(Collectors.toList());

        //  添加订单明细.
        map.put("details",maps);

        return map;
    }

    @Override
    public List<OrderInfo> orderSplit(String orderId, String wareSkuMap) {
        /*
        1.  先获取到原始订单。  orderId
        2.  wareSkuMap 存储的是仓库Id 与 skuId 的对照关系！
            [{"wareId":"1","skuIds":["2","10"]},{"wareId":"2","skuIds":["3"]}]  List<Map>
            需要将这个参数变为可以操作的对象 例如 map class 等.
        3.  创建新的子订单对象 并赋值
        4.  保存好子订单， 订单的状态不需要手动更改，只需要支付成功之后，通过mq 发送消息的形式 ，自动更改状态.
        5.  将子订单保存到集合中
        6.  修改原始订单状态. SPLIT;
        7.  测试.

         */
        //  声明一个集合
        List<OrderInfo> subOrderInfoList = new ArrayList<>();
        OrderInfo originOrderInfo = this.getOrderInfo(Long.parseLong(orderId));
        //  数据转换
        List<Map> mapList = JSON.parseArray(wareSkuMap, Map.class);
        if (!CollectionUtils.isEmpty(mapList)){
            //  获取到对应的仓库Id
            for (Map map : mapList) {
                String wareId = (String) map.get("wareId");
                List<String> skuIdsList = (List<String>) map.get("skuIds");

                //  创建子订单并赋值
                OrderInfo suborderInfo = new OrderInfo();
                BeanUtils.copyProperties(originOrderInfo,suborderInfo);
                //  Id 设置为空
                suborderInfo.setId(null);
                //  设置父Id
                suborderInfo.setParentOrderId(Long.parseLong(orderId));
                //  设置仓库Id
                suborderInfo.setWareId(wareId);
                //  计算每个子订单的总金额
                /*
                1.  计算子订单总金额，必须要找到子订单对应的明细。
                    如何获取到子订单的明细。
                 */
                List<OrderDetail> orderDetails = new ArrayList<>();
                List<OrderDetail> orderDetailList = originOrderInfo.getOrderDetailList();
                for (OrderDetail orderDetail : orderDetailList) {
                    for (String skuId : skuIdsList) {
                        if (orderDetail.getSkuId()==Long.parseLong(skuId)){
                            //  子订单明细
                            orderDetails.add(orderDetail);
                        }
                    }
                }
                suborderInfo.setOrderDetailList(orderDetails);
                suborderInfo.sumTotalAmount();
                //  保存子订单：
                this.saveOrderInfo(suborderInfo);
                //  将子订单添加到集合中
                subOrderInfoList.add(suborderInfo);

            }
        }

        //  修改订单状态:
        this.updateOrderStatus(Long.parseLong(orderId),ProcessStatus.SPLIT);
        //  返回数据.
        return subOrderInfoList;
    }

    @Override
    public void execExpiredOrder(Long orderId, String flag) {
        //  进行优化：
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);

        if ("2".equals(flag)){
            //  发送消息关闭paymentInfo
            this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
        }

    }
}
